﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;
using System.Drawing;
using ALDIOrganizer.Properties;

namespace ALDIOrganizer
{
 
    public partial class SheetView
    {
        private Rectangle dragBoxFromMouseDown;
        private int colSRC;
        private int rowSRC;

        private int colOVR;
        private int rowOVR;

        private int colDES;
        private int rowDES;

        public enum DragAction { move, overwrite, duplicate, swap };

        internal void DoTheDrop(int colSRC, int rowSRC,
            int colDES, int rowDES,
            DragAction action)
        {
            var _sourceCell = Underlying(colSRC, rowSRC);
            var _destinationCell = Underlying(colDES, rowDES);
            var dest_IsSHIFT = (_destinationCell.MyCellType == CellType.Shift);

            var copyofDES = new ShiftTandem();
            var copyofSRC = new ShiftTandem();
            if (dest_IsSHIFT)
                copyofDES = (ShiftTandem)_destinationCell.Shifts.Clone();            
            copyofSRC = (ShiftTandem)_sourceCell.Shifts.Clone();            

            var notesSRC = _sourceCell.HasAnyNotes ? _sourceCell.Notes : String.Empty;            
            var notesDES = _destinationCell.HasAnyNotes ? _destinationCell.Notes : String.Empty;

            // check if working hours match
            var _destHRS = new WorkingHoursSpan();

            /* IMPORTANT  BIT. Shifts know their parent (or: the Sheet instance they belong to)
             * and they refer to it to figure out what their working hours are.
             * (When a shift is set to "open - close", or e.g. "15:00 - close",
             * it needs to know the hour when "close" happens.
             * 
             * We are operating on deep copies of shifts that are being drag'n'dropped,
             * and our implementation of Clone (deep copying via serialization) does not
             * include GetParent, which is marked as NonSerializable.             
             * 
             * There's no point whatsoever in serializing / copying it, since it is too
             * big and complex of a class.
             * 
             * So we need to manually "reset" their parent here. */             
             
            foreach (var s in copyofSRC.ToArray().Concat(copyofDES.ToArray()))
            {
                s.Parent = refill;
            }

            switch (Mode)
            {
                case SheetType.Rota:
                    {
                        _destHRS = refillRota.GetWorkingHours[colDES - 1];
                        break;
                    }
                case SheetType.Template:
                    {
                        _destHRS = refill_template.WorkingHours[colDES - 1];
                        break;
                    }
                default: break;
            }
            
            
            var sourceDay = (Weekday)(colSRC - 1);
            var destinationDay = (Weekday)(colDES - 1);

            switch (Shift.WhatMismatch(
                copyofSRC.ToArray(),
                _destHRS))
            {                
                case Mismatch.SticksOut:
                    {
                        MessageBox.Show(
                            Resources.MESSAGEBOX_INFORMATION_ShiftsMismatch_SticksOut_MESSAGE.Replace(
                            "#",
                            Time.NameOfDay(destinationDay)),
                            Resources.MESSAGEBOX_INFORMATION_ShiftsMismatch_CAPTION,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
                        break;
                    }
                case Mismatch.OutCompletely:
                    {
                        MessageBox.Show(
                            Resources.MESSAGEBOX_INFORMATION_ShiftsMismatch_OutCompletely_MESSAGE,
                            Resources.MESSAGEBOX_INFORMATION_ShiftsMismatch_CAPTION,
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
                        return;
                    }
                default: break;
            }

            

            refill.SuppressModifiedChanged();

            // Always clear the destination cell... unless it's empty. If there's no shift there, we do nothing
            if (dest_IsSHIFT)
            {
                if (refill.Mode == SheetType.Template)
                {
                    refill_template.RemoveShifts(destinationDay, _destinationCell.Tag, false);
                }
                else
                {
                    refillRota.RemoveShifts(destinationDay, _destinationCell.Tag, false);
                }
            }
            
            // If we are duplicating, we're leaving the source cell untouched.
            if (action != DragAction.duplicate)
            {
                if (refill.Mode == SheetType.Template)
                {
                    refill_template.RemoveShifts(sourceDay, _sourceCell.Tag, false);
                }
                else
                {
                    refillRota.RemoveShifts(sourceDay, _sourceCell.Tag, false);
                }
            }
            // If we are swapping, we copy the ("remembered") destination value into the source cell
            if (action == DragAction.swap)
            {
                _sourceCell.Notes = notesDES;
                if (Mode == SheetType.Rota)
                {
                    refillRota.AddShifts(copyofDES, sourceDay, _sourceCell.Tag, false);
                }
                else
                {
                    refill_template.AddShifts(copyofDES, sourceDay, _sourceCell.Tag, false);
                }
            }

            refill.ResumeModifiedChanged();
            // we always copy the source cell into the destination cell. In any case.
            if (Mode == SheetType.Rota)
            {
                refillRota.AddShifts(copyofSRC, destinationDay, _destinationCell.Tag, true);
            }
            else
            {
                refill_template.AddShifts(copyofSRC, destinationDay, _destinationCell.Tag, true);
            }
            _destinationCell.Notes = notesSRC;

            #region preparing arguments - and calling Refill_OnDataChanged
            var _tags = new RowTag[] { _sourceCell.Tag,
                _destinationCell.Tag };
            var _days = new Weekday[] { sourceDay,
                destinationDay };
            // eliminate duplicates
            _tags = _tags.Distinct().ToArray();            
            _days = _days.Distinct().ToArray();
            Refill_OnDataChanged(_tags, _days);
            #endregion

            if (Mode == SheetType.Rota)
            {
                foreach (var _tag in _tags)
                {
                    refillRota.Employees.RefreshEmployee(_tag.Int64Value);
                    RefreshEmployeesHours(_tag.Int64Value);
                }
                refillRota.UpdateBottomBar(_days);
                
            }                                    
            CurrentCell = this[colDES,rowDES];            
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            if ((e.Button & MouseButtons.Left) != MouseButtons.Left)
            {
                return;
            }
            if (dragBoxFromMouseDown != Rectangle.Empty && !dragBoxFromMouseDown.Contains(e.X, e.Y))
            {
                DoDragDrop(this[colSRC, rowSRC].Value, DragDropEffects.All);
            }
        }

        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (ReadOnly)
            {
                return;
            }
            var hitTest = HitTest(e.X, e.Y); // why call it twice
            rowSRC = hitTest.RowIndex;
            colSRC = hitTest.ColumnIndex;

            if (rowSRC == -1 ||
                colSRC == -1)
            {
                dragBoxFromMouseDown = Rectangle.Empty;
            }
            else
            {
                if (Underlying(colSRC, rowSRC).MyCellType != CellType.Shift)
                {
                    dragBoxFromMouseDown = Rectangle.Empty;
                }
                else
                {
                    var dragSize = SystemInformation.DragSize;
                    dragBoxFromMouseDown = new Rectangle(
                        new Point(
                            e.X - (dragSize.Width / 2),
                            e.Y - (dragSize.Height / 2)),
                        dragSize);
                }
            }
        }

        protected override void OnDragOver(DragEventArgs drgevent)
        {
            Point p = PointToClient(new Point(drgevent.X, drgevent.Y));
            var hit = HitTest(p.X, p.Y);

            if (hit.Type != DataGridViewHitTestType.Cell)
            {
                drgevent.Effect = DragDropEffects.None;
            }
            else
            {
                colOVR = hit.ColumnIndex;
                rowOVR = hit.RowIndex;
                var overCell = Underlying(colOVR, rowOVR);

                /* We cannot drop a Cell onto itself own.
				So we're checking here whether we are "away from home" */
                bool theSameCell = ((colOVR == colSRC) && (rowOVR == rowSRC));

                if ((!overCell.IsEditable) | (theSameCell))
                {
                    drgevent.Effect = DragDropEffects.None;
                }
                else
                {
                    // Is Ctrl pressed?
                    drgevent.Effect = (drgevent.KeyState & 8) != 0 ? DragDropEffects.Copy : DragDropEffects.Move;
                }
            }
        }

        protected override void OnDragDrop(DragEventArgs drgevent)
        {
            var clientPoint = PointToClient(new Point(drgevent.X, drgevent.Y));
            var hitInfo = HitTest(clientPoint.X, clientPoint.Y);
            colDES = hitInfo.ColumnIndex;
            rowDES = hitInfo.RowIndex;

            var _destinationCell = Underlying(colDES, rowDES);
            var _destCellHasShifts = (_destinationCell.MyCellType == CellType.Shift);
            var dr = DragAction.move;
            if ((drgevent.Effect & DragDropEffects.Move) == DragDropEffects.Move &&
                _destCellHasShifts)
            {
                dr = DragAction.overwrite;
            }
            // else _foo = DragAction.move; - that'pShift the default Value
            if ((drgevent.Effect & DragDropEffects.Copy) == DragDropEffects.Copy) // user pressed Ctrl
            {
                dr = _destCellHasShifts ? DragAction.swap : DragAction.duplicate;
            }
            DoTheDrop(colSRC, rowSRC, colDES, rowDES, dr);
        }

        

    
    }
}
